package tech

import (
	"ssp/set"
)

func (s *Solve) D3y(g *Grid) (used bool, results []*Result) {
	return g.d3y()
}

func (g *Grid) d3y() (used bool, results []*Result) {
	for _, box := range g.boxes {
		for dnum := range 3 {
			boxCells := []*Cell{}
			rowCells := []*Cell{}
			shareCells := []*Cell{}
			for _, cell := range box.cells {
				if cell.solved() {
					continue
				}
				if cell.rowNum == box.cells[0].rowNum+dnum {
					shareCells = append(shareCells, cell)
				} else {
					boxCells = append(boxCells, cell)
				}
			}
			for _, cell := range g.rows[box.cells[0].rowNum+dnum].cells {
				if cell.solved() {
					continue
				}
				if cell.boxNum != box.cells[0].boxNum {
					rowCells = append(rowCells, cell)
				}
			}
			boxHiddenCandss, boxHiddenCells := findHiddenNaked(boxCells)
			if len(boxHiddenCandss) == 0 {
				continue
			}
			rowHiddenCandss, rowHiddenCells := findHiddenNaked(rowCells)
			if len(rowHiddenCandss) == 0 {
				continue
			}
			for i, boxHiddenCands := range boxHiddenCandss {
				boxHiddenCell := boxHiddenCells[i]
				for j, rowHiddenCands := range rowHiddenCandss {
					rowHiddenCell := rowHiddenCells[j]
					if boxHiddenCell.cands.Count() == 2 && rowHiddenCell.cands.Count() == 2 {
						continue
					}
					if boxHiddenCands.Intersect(rowHiddenCands).Count() != 0 {
						continue
					}
					//fmt.Println(boxHiddenCell, rowHiddenCell)
					if !verifyD3yShare(rowHiddenCands.Union(boxHiddenCands), shareCells, boxCells, rowCells) {
						continue
					}
					used = true
					if OUTPUTRESULT {
						result := Result{}
						if boxHiddenCell.cands.Count() > 2 {
							result.addRemoved(boxHiddenCell.cands.Diff(boxHiddenCands), []*Cell{boxHiddenCell})
						}
						if rowHiddenCell.cands.Count() > 2 {
							result.addRemoved(rowHiddenCell.cands.Diff(rowHiddenCands), []*Cell{rowHiddenCell})
						}
						g1 := result.getHelpCandsGroup()
						g1.add(boxHiddenCands, []*Cell{boxHiddenCell})
						g1.add(boxHiddenCands, shareCells)
						g2 := result.getHelpCandsGroup()
						g2.add(rowHiddenCands, []*Cell{rowHiddenCell})
						g2.add(rowHiddenCands, shareCells)
						result.addHelpCells(shareCells)
						result.addHelpCells([]*Cell{rowHiddenCell})
						result.addHelpCells([]*Cell{boxHiddenCell})
						result.setStrategyName("D3y")
						results = append(results, &result)
					}
					if CHANGE {
						removeCands(boxHiddenCands, []*Cell{boxHiddenCell})
						removeCands(rowHiddenCands, []*Cell{rowHiddenCell})
					}
					if !EXHAUSTIVE {
						return
					}
				}
			}
		}
	}
	return
}

func verifyD3yShare(cands set.Set, shareCells, boxCells, rowCells []*Cell) (ok bool) {
	if len(shareCells) == 2 {
		return true
	}
	shareCands := unionCellCands(shareCells)
	if shareCands == cands {
		return true
	}
	zcands := shareCands.Diff(cands)
	boxCands := unionCellCands(boxCells)
	if boxCands.Intersect(zcands).Count() == 0 {
		return true
	}
	rowCands := unionCellCands(rowCells)
	if rowCands.Intersect(zcands).Count() == 0 {
		return true
	}

	return false
}
func findHiddenNaked(cells []*Cell) (hiddenCandss []set.Set, hiddenCells []*Cell) {
	for i, hiddenCell := range cells {
		others := []*Cell{}
		others = append(others, cells[:i]...)
		others = append(others, cells[i+1:]...)
		hiddenCands := hiddenCell.cands.Diff(unionCellCands(others))
		if hiddenCands.Count() == 2 {
			hiddenCandss = append(hiddenCandss, hiddenCands)
			hiddenCells = append(hiddenCells, hiddenCell)
		}
	}
	return
}
